package com.environment.component;

import com.environment.Environment;
import com.environment.exception.MissingComponentException;
import com.environment.unit.Component;
import com.environment.unit.IComponent;
import com.jme.image.Texture;
import com.jme.scene.state.FogState;
import com.jme.scene.state.TextureState;
import com.jmex.effects.LensFlare;
import com.jmex.effects.LensFlareFactory;
import com.jmex.effects.glsl.BloomRenderPass;

/**
 * <code>EffectComponent</code> extends <code>Component</code> and maintains
 * all special effects of <code>Environment</code>.
 * <p>
 * The maintained effects include lens flare, full screen blooming and per-vertex
 * fog effects.
 * <p>
 * Lens flare <code>Texture</code> array has to be set prior to connecting to
 * <code>Environment</code>.
 * <p>
 * <code>EffectComponent</code> does not require any connections with other
 * <code>Component</code> before initialization.
 * 
 * @author Yi Wang (Neakor)
 * @version Creation date: 04-10-2008 21:25 EST
 * @version Modified date: 06-15-2008 20:22 EST
 */
public class EffectComponent extends Component{
	/**
	 * Serial version.
	 */
	private static final long serialVersionUID = -7128023334924574094L;
	/**
	 * The reference to <code>Environment</code> instance.
	 */
	private Environment environment;
	/**
	 * The day time fog color.
	 */
	private final float[] dayFogColor;
	/**
	 * The nigh time fog color.
	 */
	private final float[] nightFogColor;
	/**
	 * The dawn and dusk fog color.
	 */
	private final float[] dawnFogColor;
	/**
	 * The <code>LensFlare</code> effect instance.
	 */
	private LensFlare lensflare;
	/**
	 * The <code>BloomRenderPass</code> for sun and moon.
	 */
	private BloomRenderPass bloom;
	/**
	 * The per-vertex <code>FogState</code> instance.
	 */
	private FogState fog;
	/**
	 * The array of lens flare <code>Texture</code>.
	 */
	private Texture[] flareTextures;
	/**
	 * The temporary fog color array.
	 */
	private final float[] tempColor;

	/**
	 * Constructor of <code>EffectComponent</code>.
	 */
	public EffectComponent() {
		super(EffectComponent.class.toString());
		this.dayFogColor = new float[]{0.6f, 0.6f, 0.6f, 0.02f};
		this.nightFogColor = new float[]{0.13725f,0.24805f,0.4098f,0.05f};
		this.dawnFogColor = new float[]{0.6343f, 0.5298f, 0.4f, 0.02f};
		this.tempColor = new float[4];
	}

	@Override
	public boolean validate() throws MissingComponentException{
		return true;
	}

	@Override
	protected void initializeDetail() {
		this.buildLensFlare();
		this.buildBlooming();
		this.buildFogState();
	}

	/**
	 * Build the lens flare effect.
	 */
	private void buildLensFlare() {
		TextureState[] texStates = new TextureState[this.flareTextures.length];
		for(int i = 0; i < this.flareTextures.length; i++) {
			texStates[i] = this.environment.getRenderer().createTextureState();
			texStates[i].setTexture(this.flareTextures[i]);
			texStates[i].setEnabled(true);
		}
		this.lensflare = LensFlareFactory.createBasicLensFlare("LensFlareEffect", texStates);
		this.lensflare.setIntensity(0);
	}

	/**
	 * Build the full screen blooming effect.
	 */
	private void buildBlooming() {
		this.bloom = new BloomRenderPass(this.environment.getRenderer().getCamera(), 5);
		this.bloom.setUseCurrentScene(true);
		this.bloom.setBlurIntensityMultiplier(0.9f);
		this.bloom.setBlurSize(0.02f);
	}

	/**
	 * Build the fog effect.
	 */
	private void buildFogState() {
		this.fog = this.environment.getRenderer().createFogState();
		this.fog.setDensity(0.00001f);
		this.fog.setDensityFunction(FogState.DensityFunction.Linear);
		this.fog.setQuality(FogState.Quality.PerVertex);
		this.fog.setStart(0.2f*this.environment.getRenderer().getCamera().getFrustumFar());
		this.fog.setEnd(this.environment.getRenderer().getCamera().getFrustumFar());
		this.fog.setEnabled(true);
	}

	@Override
	protected void updateDetail(float time) {
		this.updateLensFlare(time);
		this.updateFogColor(time);
	}
	
	/**
	 * Update the lens flare effect intensity based on the current time.
	 * @param time The current time of the environment in 24 hour time system.
	 */
	private void updateLensFlare(float time) {
		// If the current time is between 8:00 and 12:00, increase the intensity.
		if(time >= 8 && time < 12) {
			this.lensflare.setIntensity((time-8)/4);
		// Else if the current time is between 12:00 and 16:00, decrease the intensity.
		} else if(time >= 12 && time < 16) {
			this.lensflare.setIntensity(1-(time-12)/4);
		// Else if the current time is equal or greater than 16:00 or less than 8:00, disable the sun's lens flare.
		} else if(time >= 16 || time < 8) {
			this.lensflare.setIntensity(0);
		}
	}

	/**
	 * Update the fog color to be the same as the ambient lighting color.
	 * @param time The current time of the environment in 24 hour time system.
	 */
	private void updateFogColor(float time) {
		// If the current time is between 6:00 and 8:00, change color towards day time color.
		if(time > 6 && time <= 8) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.dawnFogColor[i] + (this.dayFogColor[i]-this.dawnFogColor[i])*(time-6)/2;
			}
			this.setFogColor();
		// Day time.
		} else if(time > 8 && time <= 16) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.dayFogColor[i];
			}
			this.setFogColor();
		// Else if the current time is between 16:00 and 18:00, change color towards dawn color.
		} else if((time > 16 && time <= 18)) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.dayFogColor[i] + (this.dawnFogColor[i]-this.dayFogColor[i])*(time-16)/2;
			}
			this.setFogColor();
		// Else if the current time is between 18:00 and 20:00, change color towards night time color.
		} else if(time > 18 && time <= 20) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.dawnFogColor[i] + (this.nightFogColor[i]-this.dawnFogColor[i])*(time-18)/2;
			}
			this.setFogColor();
		// Night time.
		} else if(time > 20 || time <= 4) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.nightFogColor[i];
			}
			this.setFogColor();
		// Else if the current time is between 4:00 and 6:00, change color towards dawn color.
		} else if(time > 4 && time <= 6) {
			for(int i = 0; i < this.tempColor.length; i++) {
				this.tempColor[i] = this.nightFogColor[i] + (this.dawnFogColor[i]-this.nightFogColor[i])*(time-4)/2;
			}
			this.setFogColor();
		}
	}
	
	/**
	 * Set the <code>FogState</code> color to the temporary color.
	 */
	private void setFogColor() {
		this.fog.getColor().r = this.tempColor[0];
		this.fog.getColor().g = this.tempColor[1];
		this.fog.getColor().b = this.tempColor[2];
		this.fog.getColor().a = this.tempColor[3];
		this.fog.getColor().clamp();
	}
	
	@Override
	public void connect(IComponent component) {
		if(component instanceof Environment) {
			this.environment = (Environment)component;
		}
	}

	/**
	 * Set the intensity of the blooming effect.
	 * @param intensity The intensity of the blooming effect.
	 */
	public void setBloomIntensity(float intensity) {
		this.bloom.setBlurIntensityMultiplier(intensity);
	}

	/**
	 * Set the array of four <code>Texture</code> used by lens flare effect.
	 * @param textures The array of four <code>Texture</code> to be set.
	 */
	public void setFlareTextures(Texture[] textures) {
		this.flareTextures = textures;
	}
	
	/**
	 * Enable or disable the full screen blooming effect.
	 * @param enabled True if enable. False disable.
	 */
	public void setBloomingEnabled(boolean enabled) {
		this.bloom.setEnabled(enabled);
	}
	
	/**
	 * Enable or disable the fog effect.
	 * @param enabled True if enable. False disable.
	 */
	public void setFogStateEnabled(boolean enabled) {
		this.fog.setEnabled(enabled);
	}
	
	/**
	 * Retrieve the <code>LensFlare</code> effect.
	 * @return The <code>LensFlare</code> effect.
	 */
	public LensFlare getLensFlare() {
		return this.lensflare;
	}
	
	/**
	 * Retrieve the <code>BloomRenderPass</code> object.
	 * @return The <code>BloomRenderPass</code> object.
	 */
	public BloomRenderPass getBlooming() {
		return this.bloom;
	}
	
	/**
	 * Retrieve the per-vertex <code>FogState</code>.
	 * @return The per-vertex <code>FogState</code>.
	 */
	public FogState getFogState() {
		return this.fog;
	}
}
